package linkageclient

import (
	"container/list"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net"
	"os"
	"sdk/fsm"
	"sdk/global"
	"sdk/pkt"
	"sdk/sdkinterface"
	"sdk/sdklog"
	"sdk/sdktype"
	"sdk/utils"
	"sync"
	"time"
	// "strings"
)

var (
	g_loger           *sdklog.Logger   = sdklog.New("linkage_client: ")
	g_linkage_session *linkage_session = nil
	g_conf            la_conf          = la_conf{}
)

type linkage_session struct {
	name            string
	client_id       uint32
	device_id       uint32
	my_request_id   uint32
	peer_request_id uint32
	select_enc      uint16
	version         uint8
	rand1           [4]uint8
	rand2           [4]uint8
	r1              uint32
	user_name       [sdktype.UUID_LEN]uint8

	//ip port
	ip        uint32
	port      uint16
	ds_doname string

	//发送链表
	send_list *list.List
	//加个锁保护链表
	smutex sync.Mutex

	//是否退出
	quit bool

	//状态机
	fsm *fsm.Machine

	//延时设置
	time_param sdktype.Uc_time_param_item_t
	conn       *net.Conn

	aes_key [sdktype.MD5_LEN]uint8

	//加个通道方便通知
	notify chan interface{}

	//user id
	user_id uint32
}

//conf
type la_conf struct {
	User   string
	Passwd string
}

func (l *linkage_session) fsmInit() {
	l.fsm = fsm.New()
	l.fsm.AddState(sdktype.UCLAS_IDLE, gIdle)
	l.fsm.AddState(sdktype.UCLAS_DIS_PROB, gDisprob)
	l.fsm.AddState(sdktype.UCLAS_DISPATCH, gDispatch)
	l.fsm.AddState(sdktype.UCLAS_AUTH, gAuth)
	l.fsm.AddState(sdktype.UCLAS_ANSWER, gAnswer)
	l.fsm.AddState(sdktype.UCLAS_ESTABLISH, gEstablish)
	l.fsm.AddState(sdktype.UCLAS_ERROR, gError)
	l.fsm.AddState(sdktype.UCLAS_SLEEP, gSleep)
	l.fsm.AddEndState(sdktype.UCLAS_QUIT)
	l.fsm.SetStartState(sdktype.UCLAS_IDLE)
}

func (l *linkage_session) IsOnline() bool {
	if l.fsm.GetCurState() == sdktype.UCLAS_ESTABLISH {
		return true
	}

	return false
}

func (l *linkage_session) Init(v ...interface{}) error {
	g_loger.Debug("linkage init")
	//初始化配置
	confInit()
	//状态机初始化
	l.fsmInit()

	return nil
}

func (l *linkage_session) Run(v ...interface{}) {
	l.fsm.Execute(l)
}

func (l *linkage_session) Exit() {
	g_loger.Info("linkage exit")
	l.quit = true
}

func (l *linkage_session) ucla_request_add(pkt *pkt.Pkt_t) bool {
	if !l.IsOnline() {
		return false
	}
	if l.ucla_enc_pkt(pkt) {
		l.my_request_id += 1
		l.putPkt(pkt)
	}

	return true
}

const (
	ucph_hdr_plain_size = 12
)

func (l *linkage_session) ucla_enc_pkt(ppkt *pkt.Pkt_t) bool {
	if l.select_enc == sdktype.UC_ENC_AES128 {
		//加密
		enc_data := utils.AesEncryptECB(ppkt.Data[ucph_hdr_plain_size:], l.aes_key[0:])
		var hdr pkt.Ucph_t
		hdr.UnPack(ppkt.Data[0:])
		hdr.Encrypt = true
		hdr.Pack(ppkt.Data[0:])
		ppkt.Total = uint32(len(enc_data)) + ucph_hdr_plain_size
		new_data := make([]byte, ppkt.Total)
		copy(new_data[0:ucph_hdr_plain_size], ppkt.Data[0:ucph_hdr_plain_size])
		copy(new_data[ucph_hdr_plain_size:], enc_data)
		ppkt.Data = new_data
	}

	return true
}

func (l *linkage_session) getPkt() *pkt.Pkt_t {
	l.smutex.Lock()
	defer l.smutex.Unlock()
	f := l.send_list.Front()
	if f != nil {
		l.send_list.Remove(f)
		return f.Value.(*pkt.Pkt_t)
	}

	return nil
}

func (l *linkage_session) putPkt(pkt *pkt.Pkt_t) bool {
	l.smutex.Lock()
	defer l.smutex.Unlock()

	l.send_list.PushBack(pkt)

	return true
}

func (l *linkage_session) resetData() {
	l.client_id = 0
	l.device_id = 0
	l.my_request_id = 0
	l.peer_request_id = 0
}

func (l *linkage_session) resetSend() {
	l.smutex.Lock()
	defer l.smutex.Unlock()

	l.send_list = list.New()
}

func (l *linkage_session) closeConn() {
	if l.conn != nil {
		(*l.conn).Close()
		l.conn = nil
	}
}

//联动会话只需要一个单例
func GetInstance() sdkinterface.Sessionner {
	if g_linkage_session == nil {
		g_linkage_session = &linkage_session{}
		g_linkage_session.name = "la"
		g_linkage_session.time_param = sdktype.Uc_time_param_item_t{Retry_100ms: [3]uint8{10, 30, 30}, Keeplive: 20, Die: 30}
		g_linkage_session.notify = make(chan interface{}, 100)
	}

	return g_linkage_session
}

func confInit() {
	cur_dir := utils.GetExePath()
	conf_path := cur_dir + "/conf/conf_json.txt"
	data, err := ioutil.ReadFile(conf_path)
	if err != nil {
		return
	}
	json.Unmarshal(data, &g_conf)
	g_loger.Debug("get user ", g_conf.User, " passwd ", g_conf.Passwd)
}

func confSave() {
	cur_dir := utils.GetExePath()
	conf_dir := cur_dir + "/conf"
	os.MkdirAll(conf_dir, 0666)
	conf_path := conf_dir + "/conf_json.txt"
	data, _ := json.Marshal(&g_conf)
	ioutil.WriteFile(conf_path, data, 0666)
}

func goto_error(cargo interface{}) (string, interface{}) {
	return sdktype.UCLAS_QUIT, cargo
}

func goto_idle(cargo interface{}) (string, interface{}) {
	return sdktype.UCLAS_IDLE, cargo
}

func gIdle(cargo interface{}) (string, interface{}) {
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()
	l.resetData()
	if g_conf.User == "" ||
		g_conf.Passwd == "" {
		return sdktype.UCLAS_ERROR, cargo
	}
	time.Sleep(time.Second * 3)
	l.closeConn()

	return sdktype.UCLAS_DIS_PROB, cargo
}

func gDisprob(cargo interface{}) (string, interface{}) {
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()
	l.resetData()

	var tlv_buf [1024]byte
	var tlv_len int = 0
	//pip，为0
	tlv_len = 4
	var tlv pkt.UcTlv = pkt.UcTlv{0, 0}
	//appid
	tlv.Type = sdktype.UCT_SERVER_PROBE_APP_ID
	tlv.Len = 4
	tlv_len += tlv.Pack(tlv_buf[tlv_len:])
	binary.BigEndian.PutUint32(tlv_buf[tlv_len:], global.G_clpriv.Appid)
	tlv_len += 4
	//vvid
	tlv.Type = sdktype.UCT_SERVER_PROBE_VVID
	tlv.Len = 4
	tlv_len += tlv.Pack(tlv_buf[tlv_len:])
	binary.BigEndian.PutUint32(tlv_buf[tlv_len:], global.G_clpriv.Vvid)
	tlv_len += 4

	var pkt *pkt.Pkt_t = pkt.Pkt_new(sdktype.CMD_UDP_DONAME_PROB, uint32(tlv_len), sdktype.TP_USER)
	// g_loger.Debug("pkt.Hlen ", pkt.Hlen, " tlv_len ", tlv_len)
	copy(pkt.Data[pkt.Hlen:], tlv_buf[0:tlv_len])
	ports := []int{global.DISP_PROB_PORT3, global.DISP_PROB_PORT2, global.DISP_PROB_PORT1}
	//本地址址
	conn, err := net.ListenUDP("udp", nil)
	if err != nil {
		g_loger.Debug("disprobe conn create failed err=", err)
		os.Exit(-1)
	}
	defer conn.Close()
	//循环等待处理disp prob fast ，一直到成功为止
	for {
		//往8个域名发
		for _, doname := range global.G_doname {
			for _, port := range ports {
				dstserver := fmt.Sprintf("%s:%d", doname, port)
				dstaddr, err := net.ResolveUDPAddr("udp", dstserver)
				if err != nil {
					g_loger.Debug("ResolveUDPAddr dstserver", dstserver, " err=", err)
					continue
				}

				n, err := conn.WriteToUDP(pkt.Data, dstaddr)
				if err != nil {
					g_loger.Debug("WriteToUdp failed dstaddr", dstaddr, " err=", err, " n=", n)
				}
			}
		}
		rbuf := make([]byte, 1024)
		for {
			conn.SetReadDeadline(time.Now().Add(time.Second * 3))
			n, raddr, err := conn.ReadFromUDP(rbuf)
			if err != nil {
				g_loger.Debug("readfromudp failed!!!!!!!!!!!!!!!!!!!!!")
				break
			}
			g_loger.Debug("read from=", raddr, " n= ", n)
			g_loger.Debugf("read data=0x%x", rbuf[0:n])
			if l.disProbPktProc(rbuf[0:n]) {
				return sdktype.UCLAS_DISPATCH, cargo
			}
		}
	}

	return goto_idle(cargo)
}

func gDispatch(cargo interface{}) (string, interface{}) {
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()
	l.resetData()

	ports := []int{global.DISP_SERVER_PORT3, global.DISP_SERVER_PORT2, global.DISP_SERVER_PORT1}
	var buf [1024]byte
	var hdr pkt.Ucla_req_disp_hdr_t
	//构包
	hdr.Apptype = 4
	hdr.Time_xor = uint32(time.Now().Unix())
	g_loger.Debug("disp proc time_xor=", hdr.Time_xor)
	hdr.Time_xor ^= 0xffffffff
	index := 0
	index += hdr.Pack(buf[index:])
	var pkt *pkt.Pkt_t = pkt.Pkt_new(sdktype.CMD_APP_SERVER_DISP, uint32(index), sdktype.TP_USER)
	copy(pkt.Data[pkt.Hlen:], buf[0:index])
	g_loger.Debug("pkt hlen=", pkt.Hlen, " index ", index)
	g_loger.Debugf("pkt hex 0x%x\n", pkt.Data)

	//再准备发包
	for i := 0; i < 3; i++ {
		//三个端口
		addr := fmt.Sprintf("%s:%d", "cn.ice.galaxywind.com", ports[i])

		if doDispAddrProc(addr, pkt, l) {
			g_loger.Debug("goto auth now !!!!!!!!!!!!!!!!!!!!!!!!")
			return sdktype.UCLAS_AUTH, cargo
		}
	}

	return goto_idle(cargo)
}

func gAuth(cargo interface{}) (string, interface{}) {
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()
	l.resetData()

	var aua pkt.Uc_app_user_auth_t
	//构包
	aua.Action = sdktype.UCAU_AUTH
	if l.r1 == 0 {
		l.r1 = uint32(rand.Int31())
		binary.BigEndian.PutUint32(l.rand1[0:], l.r1)
	}
	aua.Version = 2
	aua.Flag = 1
	copy(aua.Username[0:], []byte(g_conf.User)[0:sdktype.UUID_LEN])
	//强用户没用uuid
	// copy(aua.Uuid[0:], global.G_clpriv.Uuid[0:])
	copy(aua.Random1[0:], l.rand1[0:])
	//网络构包
	index := 0
	var buf [1024]byte
	index += aua.Pack(buf[index:])
	pkt := pkt.Ucla_pkt_new(sdktype.CMD_APP_USER, uint32(index), true, false, 0, 0, 0, 0)
	copy(pkt.Data[pkt.Hlen:], buf[0:index])
	if authSend(pkt, l) {
		return sdktype.UCLAS_ANSWER, cargo
	}

	return sdktype.UCLAS_IDLE, cargo
}

func gAnswer(cargo interface{}) (string, interface{}) {
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()

	g_loger.Debug("go to answer proc !!!!!!!!!!!!!!!!!!!!!!!!!!!!!client_id=", l.client_id)
	var tlv_buf [1024]byte
	var tlv_len int = 0
	var tlv pkt.UcTlv = pkt.UcTlv{0, 0}

	//ver
	tlv.Type = sdktype.UCLA_ANSWER_TLV_VER
	tlv.Len = 4
	tlv_len += tlv.Pack(tlv_buf[tlv_len:])
	copy(tlv_buf[tlv_len:], global.G_clpriv.Appver[0:])
	tlv_len += 4
	//appid
	tlv.Type = sdktype.UCLA_ANSWER_TLV_APPID
	tlv.Len = 4
	tlv_len += tlv.Pack(tlv_buf[tlv_len:])
	binary.BigEndian.PutUint32(tlv_buf[tlv_len:], global.G_clpriv.Appid)
	tlv_len += 4

	md5_key := utils.GetMd5([]byte(g_conf.Passwd))
	//md5
	md5 := utils.GetMd5(l.rand1[0:], l.rand2[0:], md5_key)
	g_loger.Debugf("rand1=0x%x", l.rand1)
	g_loger.Debugf("rand2=0x%x", l.rand2)
	g_loger.Debugf("md5_key=0x%x", md5_key)
	g_loger.Debugf("md5=0x%x", md5)
	//构包
	var acr pkt.Uc_app_user_question_t
	acr.Action = sdktype.UCAU_ANSWER
	acr.Support_enc = 0x01
	copy(acr.Md5[0:], md5[0:])
	acr_len := acr.PackLen()
	pkt := pkt.Ucla_pkt_new(sdktype.CMD_APP_USER, uint32(tlv_len+acr_len), true, false, 0, l.client_id, l.device_id, 0)
	len := int(pkt.Hlen)
	len += acr.Pack(pkt.Data[len:])
	copy(pkt.Data[len:], tlv_buf[0:tlv_len])
	len += tlv_len
	g_loger.Debugf("client_id=0x%x", l.client_id)
	g_loger.Debugf("answer get pkt data=0x%x", pkt.Data)

	if answerSend(pkt, l) {
		return sdktype.UCLAS_ESTABLISH, cargo
	}

	return sdktype.UCLAS_IDLE, cargo
}

func gEstablish(cargo interface{}) (string, interface{}) {
	g_loger.Debug("enter establish !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
	l, ok := cargo.(*linkage_session)
	if !ok {
		return goto_error(cargo)
	}
	l.resetSend()
	//先查询个misc报文
	l.addMiscQuery()
	l.addKeepLive()
	l.doEstablishProc()

	return sdktype.UCLAS_AUTH, cargo
}

func gError(cargo interface{}) (string, interface{}) {
	time.Sleep(time.Second * 30)

	return sdktype.UCLAS_IDLE, cargo
}

func gSleep(cargo interface{}) (string, interface{}) {
	time.Sleep(time.Second * 30)

	return sdktype.UCLAS_IDLE, cargo
}

//细节函数。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
func (l *linkage_session) disProbPktProc(rbuf []byte) bool {
	var hdr pkt.Net_Header_t
	index := 0
	index += hdr.UnPack(rbuf[index:])
	if hdr.Ver >= 2 {
		g_loger.Error("hdr ver is err ", hdr.Ver)
		return false
	}

	g_loger.Debug("ucla_disp_probe_read command=", hdr.Command)
	switch hdr.Command {
	case sdktype.CMD_UDP_DONAME_PROB:
		ip := binary.BigEndian.Uint32(rbuf[index:])
		strip := utils.InetNtoaStr(ip)
		index += 4
		ds := string(rbuf[index:])
		//取掉最后的\0
		ds = ds[0:(len(ds) - 1)]
		l.ds_doname = ds
		g_loger.Debug("get dst doname=", ds, " from=", strip)
		return true
	default:
		g_loger.Error("disp prob not proc cmd", hdr.Command)
	}

	return false
}

func dispUdpProc(hdr *pkt.Net_Header_t, rbuf []byte, l *linkage_session) bool {
	g_loger.Debug("disp udp recv buf ", rbuf)
	var res pkt.Ucla_disp_res_hdr_t
	index := 0
	index += res.UnPack(rbuf[index:])

	if res.Errorno != sdktype.ERR_NONE {
		g_loger.Debug("disp recv errno=", res.Errorno)
		return false
	}
	g_loger.Debug("disp recv ip=", utils.InetNtoaStr(res.Ip), " port=", res.Port)
	if res.Ip == 0 || res.Port == 0 {
		return false
	}
	l.ip = res.Ip
	l.port = res.Port

	return true
}

func doDispRecvPktProc(rbuf []byte, l *linkage_session) bool {
	var hdr pkt.Net_Header_t
	var ret bool = false
	index := 0
	index += hdr.UnPack(rbuf[index:])
	if hdr.Ver >= 2 {
		g_loger.Error("hdr ver is err ", hdr.Ver)
		return false
	}

	g_loger.Debug("doDispRecvPktProc command=", hdr.Command)
	switch hdr.Command {
	case sdktype.CMD_APP_SERVER_DISP:
		ret = dispUdpProc(&hdr, rbuf[index:], l)
	default:
		g_loger.Error("disp not proc cmd", hdr.Command)
	}

	return ret
}

func doDispAddrProc(addr string, pkt *pkt.Pkt_t, l *linkage_session) bool {
	g_loger.Debug("dispaddr goto dial udp dst addr=", addr)
	conn, err := net.DialTimeout("udp", addr, time.Second*3)
	if err != nil {
		g_loger.Debug("disp addr ", addr, "failed err ", err)
		return false
	}
	defer conn.Close()
	//write
	var n int
	conn.SetDeadline(time.Now().Add(time.Second * 3))
	n, err = conn.Write(pkt.Data)
	if err != nil {
		g_loger.Debug("disp addr ", addr, " write failed err ", err)
		return false
	}
	conn.SetReadDeadline(time.Now().Add(time.Second * 3))
	rbuf := make([]byte, 1024)
	n, err = conn.Read(rbuf)
	if err != nil {
		g_loger.Debug("disp addr ", addr, " read failed err ", err)
		return false
	}
	g_loger.Debug("disp addr proc recv n=", n, " from ", conn.RemoteAddr().String())
	return doDispRecvPktProc(rbuf[0:n], l)
}

//auth 报文发送处理
func authSend(pkt *pkt.Pkt_t, l *linkage_session) bool {
	ports := [3]int{global.UCLA_PORT2, global.UCLA_PORT1, global.UCLA_PORT}

	for i := 0; i < 3; i++ {
		dstaddr := fmt.Sprintf("%s:%d", utils.InetNtoaStr(l.ip), ports[i])
		// dstaddr := fmt.Sprintf("%s:%d", "10.135.255.201", ports[i])
		g_loger.Debug("auth goto dstaddr=", dstaddr, " connect")
		conn, err := net.DialTimeout("udp", dstaddr, time.Second*2)
		if err != nil {
			g_loger.Debug("auth dstaddr=", dstaddr, " dialtimeout failed ", err)
			continue
		}
		conn.SetDeadline(time.Now().Add(time.Second * 3))
		var n int
		n, err = conn.Write(pkt.Data[0:])
		if err != nil {
			g_loger.Debug("auth dstaddr=", dstaddr, " write failed ", err)
			conn.Close()
			continue
		}
		g_loger.Debug("auth send dstaddr=", dstaddr, " write successed n=", n)
		conn.SetReadDeadline(time.Now().Add(time.Second * 3))
		var rbuf []byte = make([]byte, 1024)
		n, err = conn.Read(rbuf)
		if err != nil || n == 0 {
			g_loger.Debug("dstaddr=", dstaddr, " read failed ", err)
			conn.Close()
			continue
		}
		g_loger.Debug("auth recv n= ", n)
		if authPktProc(rbuf[0:n], l) {
			l.conn = &conn
			l.port = uint16(ports[i])
			return true
		}
		conn.Close()
		//如果端口能通，但不成功，可能是其他问题，非网络问题，直接 失败吧
		break
	}

	return false
}

func authPktProc(rbuf []byte, l *linkage_session) bool {
	var hdr pkt.Ucph_t
	var rd pkt.Uc_app_user_answer_t

	index := 0
	index += hdr.UnPack(rbuf[index:])
	index += rd.UnPack(rbuf[index:])

	g_loger.Debug("authproc action=", rd.Action, " command=",
		hdr.Command, " client_id=", hdr.Client_sid, " device_id=",
		hdr.Device_sid)
	if hdr.Command == sdktype.CMD_UDP_KEEPLIVE {
		g_loger.Debug("auth recv kepplive so error")
		return false
	}

	switch rd.Action {
	case sdktype.UCAU_QUESTION:
		l.client_id = hdr.Client_sid
		l.device_id = hdr.Device_sid
		copy(l.rand2[0:], rd.Random2[0:])
		return true
	default:
	}

	return false
}

func answerSend(pkt *pkt.Pkt_t, l *linkage_session) bool {
	conn := *l.conn
	g_loger.Debug("enter answerSend!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!conn=", conn)
	conn.SetDeadline(time.Now().Add(time.Second * 3))
	n, err := conn.Write(pkt.Data[0:])
	if err != nil {
		g_loger.Debug("answerSend dstaddr= write failed ", err)
		return false
	}
	g_loger.Debug("answerSend send dstaddr= write successed n=", n)
	conn.SetReadDeadline(time.Now().Add(time.Second * 3))
	var rbuf []byte = make([]byte, 1024)
	n, err = conn.Read(rbuf)
	if err != nil || n == 0 {
		g_loger.Debug("answerSend= read failed ", err)
		return false
	}
	g_loger.Debug("answerSend recv n= ", n)
	if answerPktProc(rbuf[0:n], l) {
		return true
	}

	return false
}

func answerPktProc(rbuf []byte, l *linkage_session) bool {
	var hdr pkt.Ucph_t
	var rd pkt.Uc_app_user_result_t
	rl := len(rbuf)
	nl := hdr.PackLen() + rd.PackLen()
	if rl < nl {
		g_loger.Debug("answerproc len err rl=", rl, " nl=", nl)
		return false
	}
	g_loger.Debugf("answer recv data 0x%x", rbuf)
	index := 0
	index += hdr.UnPack(rbuf[index:])
	g_loger.Debug("index=", index)
	g_loger.Debugf("hdr dump=0x%x", rbuf[index:])
	index += rd.UnPack(rbuf[index:])

	g_loger.Debug("answerproc action=", rd.Action, " command=",
		hdr.Command, " client_id=", hdr.Client_sid, " device_id=",
		hdr.Device_sid)

	switch rd.Action {
	case sdktype.UCAU_RESULT:
		return answerResultProc(&hdr, &rd, l, rbuf[index:])
	default:
	}

	return false
}

func answerResultProc(hdr *pkt.Ucph_t, rd *pkt.Uc_app_user_result_t, l *linkage_session, tlv_buf []byte) bool {
	g_loger.Debug("answer result ver=", rd.Ver, " select_enc=", rd.Select_enc, " err=", rd.Err)

	// 处理tlv
	if rd.Err != 0 {
		g_loger.Debug("answer failed !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
		return false
	}
	keymd5 := utils.GetMd5([]byte(g_conf.Passwd))
	md5 := utils.GetMd5(l.rand1[0:], l.rand2[0:], keymd5, keymd5)
	l.select_enc = rd.Select_enc
	copy(l.aes_key[0:], md5[0:])
	//处理answer result tlv
	answerResultTlvProc(tlv_buf, l)
	g_loger.Debugf("answerResultProc ok and goto establish!!!!!!!!!!!!!!!!!!!!!! aes_key=0x%x", l.aes_key)

	return true
}

func answerResultTlvProc(tlv_buf []byte, l *linkage_session) {
	index := 0
	remain := int(len(tlv_buf))
	var tlv pkt.UcTlv
	t_len := tlv.PackLen()

	g_loger.Debug("answer result remain=", remain)
	for index+int(t_len) <= remain {
		index += tlv.UnPack(tlv_buf[index:])
		if index+int(tlv.Len) > remain {
			g_loger.Debug("tlv err index=", index, " tlv.type=", tlv.Type, " tlv.Len=", tlv.Len)
			return
		}
		g_loger.Debug("get tlv type=", tlv.Type)
		switch tlv.Type {
		case sdktype.UCLA_RESULT_TLV_USERID:
			if tlv.Len >= 4 {
				l.user_id = binary.BigEndian.Uint32(tlv_buf[index:])
				g_loger.Debug("get usre_id=", l.user_id)
			}
		case sdktype.UCLA_RESULT_TLV_LASTTIME:
			if tlv.Len >= 4 {
				ver := binary.BigEndian.Uint32(tlv_buf[index:])
				g_loger.Debugf("get last ver=0x%x", ver)
			}
		}

		index += int(tlv.Len)
	}
}

func (l *linkage_session) addMiscQuery() {
	var pt *pkt.Pkt_t

	pt = pkt.Ucla_pkt_new(sdktype.CMD_APP_USER, 4, true, true, 0, l.client_id, l.device_id, l.my_request_id)
	pt.Data[pt.Hlen] = sdktype.UCAU_MISC_QUERY

	l.ucla_request_add(pt)
}

func (l *linkage_session) addKeepLive() {
	var pt *pkt.Pkt_t

	pt = pkt.Ucla_pkt_new(sdktype.CMD_UDP_KEEPLIVE, 4, true, true, 0, l.client_id, l.device_id, l.my_request_id)
	pt.Data[pt.Hlen] = sdktype.UCAU_REQUEST

	l.ucla_request_add(pt)
}

func (l *linkage_session) doEstablishProc() {
	//先来个循环
	g_loger.Debug("enter doEstablishProc!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
	conn := *l.conn
	var rbuf []byte = make([]byte, 1024*10)
	timeout := 3 * time.Second
	for {
		//先发送,再接收
		var pt *pkt.Pkt_t = l.getPkt()
		g_loger.Debug("establish goto send pkt......pt=", pt)
		if pt != nil {
			//循环发送
			last_time := time.Now()
			for {
				if time.Now().Sub(last_time) > time.Duration(l.time_param.Die)*time.Second {
					g_loger.Debug("establish die now !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
					return
				}
				conn.SetDeadline(time.Now().Add(timeout))
				n, err := conn.Write(pt.Data)
				if err != nil {
					g_loger.Debug("write failed err=", err)
					continue
				}
				g_loger.Debug("establish send cmd=", pt.Cmd, " n=", n)
				// g_loger.Debugf("data:0x%x", pt.Data)

				//接收
				conn.SetReadDeadline(time.Now().Add(timeout))
				n, err = conn.Read(rbuf)
				if err != nil {
					g_loger.Debug("read failed err=", err)
					continue
				}
				g_loger.Debug("establish recv n=", n)
				//proc
				if !l.establishPktProc(rbuf[0:n]) {
					return
				}
				break
			}
		} else {
			//准备发心跳或者监听通知信道
			g_loger.Debug("establish goto notify monitor")
			var v interface{}
			select {
			case v = <-l.notify:
				g_loger.Debug("establish recv notyfy v=", v)
				break
			// case <- time.After(time.Second * time.Duration(l.time_param.Keeplive)):
			case <-time.After(time.Second * 20):
				l.addKeepLive()
				break
			}
		}
	}
}

func (l *linkage_session) establishPktProc(rbuf []byte) bool {
	len := len(rbuf)
	var hdr pkt.Ucph_t
	if len < hdr.PackLen() || len < ucph_hdr_plain_size {
		g_loger.Debug("err len=", len)
		return true
	}
	index := 0
	index += hdr.UnPack(rbuf[index:])
	g_loger.Debug("recv pkt len=", len, " entry=", hdr.Encrypt, " request=", hdr.Request, " cid=", hdr.Client_sid, " did=", hdr.Device_sid)
	if hdr.Encrypt {
		dec_data := utils.AesDecryptECB(rbuf[ucph_hdr_plain_size:], l.aes_key[0:])
		copy(rbuf[ucph_hdr_plain_size:], dec_data[0:])
		hdr.UnPack(rbuf[0:])
	}

	g_loger.Debug("establish recv command=", hdr.Command, " param_len=", hdr.Param_len)
	if l.client_id != hdr.Client_sid {
		g_loger.Debugf("drop client id is not same =", l.client_id, " hdr.client_id", hdr.Client_sid)
		return true
	}

	if hdr.Command == sdktype.CMD_UDP_KEEPLIVE && rbuf[pkt.UCPH_DHR_SIZE_LEN] == sdktype.UCAU_RESET {
		//reset,需要退出会话
		return false
	}

	if hdr.Request {
		return l.establishPktRequestProc(&hdr, rbuf[index:])
	}

	switch hdr.Command {
	case sdktype.CMD_UDP_KEEPLIVE:
		g_loger.Debug("recv keeplive now!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
	case sdktype.CMD_APP_USER:
		l.pktAppUserProc(&hdr, rbuf[index:])
	}

	return true
}

func (l *linkage_session) establishPktRequestProc(hdr *pkt.Ucph_t, rbuf []byte) bool {

	return true
}

func (l *linkage_session) pktAppUserProc(hdr *pkt.Ucph_t, rbuf []byte) bool {
	switch rbuf[0] {
	case sdktype.UCAU_MISC_QUERY:
		l.doMiscQueryProc(rbuf)
	}

	return true
}

func (l *linkage_session) doMiscQueryProc(rbuf []byte) bool {
	count := rbuf[1]
	g_loger.Debug("enter doMiscQueryProc count=", count)
	index := 4
	if count > 0 {
		cap_lastmodifytime := binary.BigEndian.Uint32(rbuf[index:])
		g_loger.Debug("doMiscQueryProc get cap_lastmodifytime=", cap_lastmodifytime)
	}
	index += 4
	if count > 1 {
		last_template_time := binary.BigEndian.Uint32(rbuf[index:])
		g_loger.Debug("doMiscQueryProc get last_template_time=", last_template_time)
	}
	index += 4
	if count > 1 {
		cap_custom_lastmodifytime := binary.BigEndian.Uint32(rbuf[index:])
		g_loger.Debug("doMiscQueryProc get cap_custom_lastmodifytime=", cap_custom_lastmodifytime)
	}

	return true
}
